package com.smsc.headend.task.engine.executor;

import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.module.common.entity.DeviceOperation;
import com.smsc.headend.module.data.enums.DictionaryType;
import com.smsc.headend.module.data.enums.OperationName;
import com.smsc.headend.task.engine.config.TaskEngineConfig;
import com.smsc.headend.task.engine.executor.impl.ReadExecutor;
import com.smsc.headend.task.engine.executor.impl.ReadWithListExecutor;
import com.smsc.headend.task.engine.executor.impl.WriteExecutor;
import com.smsc.headend.task.engine.executor.impl.ansi.*;
import com.smsc.headend.task.engine.executor.impl.collection.ReadDCUCollectionDataExecutor;
import com.smsc.headend.task.engine.executor.impl.collection.ReadProfileExecutor;
import com.smsc.headend.task.engine.executor.impl.dcu.DcuSetCollSchemeExecutor;
import com.smsc.headend.task.engine.executor.impl.dcu.DcuSetMeterTaskExecutor;
import com.smsc.headend.task.engine.executor.impl.dcu.DcuWriteDataExecutor;
import com.smsc.headend.task.engine.executor.impl.deviceconfig.*;
import com.smsc.headend.task.engine.executor.impl.firmware.*;
import com.smsc.headend.task.engine.executor.impl.generalparam.ActionGeneralParamExecutor;
import com.smsc.headend.task.engine.executor.impl.generalparam.GetGeneralParamExecutor;
import com.smsc.headend.task.engine.executor.impl.generalparam.SetGeneralParamExecutor;
import com.smsc.headend.task.engine.executor.impl.metercontrol.RemoteControlActionExecutor;
import com.smsc.headend.task.engine.executor.impl.scheduleread.ScheduleReadExecutor;
import com.smsc.headend.task.engine.executor.impl.timesync.ReadClockExecutor;
import com.smsc.headend.task.engine.executor.impl.timesync.SetClockExecutor;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * Singleton Constructed in
 *
 * @see TaskEngineConfig
 * init method to register all opertion in UDIS
 */
@Slf4j
@Component
public class AtomicTaskFactory {

    @Autowired
    RedisUtils redisUtils;

    @Autowired
    FeignAssetManagementService assetManagementRemoteService;

    @Autowired
    AutowireCapableBeanFactory autowireCapableBeanFactory;
    @Autowired
    AssetService assetService;
    private Map<OperationName, Class<TaskExecutor>> executorMap = new HashMap<>();

    @PostConstruct
    public void init() {
        this.registerExecutor(OperationName.ReadData, ReadExecutor.class);
        this.registerExecutor(OperationName.GetImageNotTransferLastBlock, ReadExecutor.class);
        this.registerExecutor(OperationName.GetFwImageBlockSize, ReadExecutor.class);
        this.registerExecutor(OperationName.GetImageTransferStatus, ReadExecutor.class);
        this.registerExecutor(OperationName.ImageTransferInit, ImageTransferInitExecutor.class);
        this.registerExecutor(OperationName.ImageBlockTransfer, ImageBlockTransferExecutor.class);
        this.registerExecutor(OperationName.ImageVerify, ImageVerifyExecutor.class);
        this.registerExecutor(OperationName.GetImagesToActivate, ReadExecutor.class);
        this.registerExecutor(OperationName.ImageActivate, FirmwareActivateExecutor.class);
        this.registerExecutor(OperationName.ReadWithList, ReadWithListExecutor.class);
        this.registerExecutor(OperationName.ReadLoadProfile, ReadProfileExecutor.class);
        this.registerExecutor(OperationName.ReadDCUCollectionData, ReadDCUCollectionDataExecutor.class);
        this.registerExecutor(OperationName.SetData, WriteExecutor.class);
        this.registerExecutor(OperationName.SetProfileCaptureObject, WriteExecutor.class);
        this.registerExecutor(OperationName.ReadDeviceClock, ReadClockExecutor.class);
        this.registerExecutor(OperationName.SetDeviceClock, SetClockExecutor.class);
        this.registerExecutor(OperationName.ReadDeviceFirmwareVersion, ReadFirmwareVersionExecutor.class);
        this.registerExecutor(OperationName.ReadProfilePeriod, ReadProfileObjPrdExecutor.class);
        this.registerExecutor(OperationName.ReadProfileCaptureObjects, ReadProfileObjPrdExecutor.class);
        this.registerExecutor(OperationName.SetProfilePeriod, SetProfileObjPrdExecutor.class);
        this.registerExecutor(OperationName.SetProfileCaptureObjects, SetProfileObjPrdExecutor.class);
        this.registerExecutor(OperationName.AnsiStopLp, SetProfileObjPrdExecutor.class);
        this.registerExecutor(OperationName.AnsiClearData, SetProfileObjPrdExecutor.class);
        this.registerExecutor(OperationName.AnsiStartLp, SetProfileObjPrdExecutor.class);
        this.registerExecutor(OperationName.MeterUpgradeSettings, UpgradeSettingsExecutor.class);
        this.registerExecutor(OperationName.DCUUpgradeSettings, UpgradeSettingsExecutor.class);
        this.registerExecutor(OperationName.MModUpgradeSettings, UpgradeSettingsExecutor.class);
        this.registerExecutor(OperationName.SModUpgradeSettings, UpgradeSettingsExecutor.class);
        this.registerExecutor(OperationName.ScheduleRead, ScheduleReadExecutor.class);
        this.registerExecutor(OperationName.GetControlMode, ReadExecutor.class);
        this.registerExecutor(OperationName.SetControlModeHardReconnect, WriteExecutor.class);
        this.registerExecutor(OperationName.SetControlModeSoftReconnect, WriteExecutor.class);
        this.registerExecutor(OperationName.RemoteControlAction, RemoteControlActionExecutor.class);
        this.registerExecutor(OperationName.SetControlModeOriginal, WriteExecutor.class);
        this.registerExecutor(OperationName.DisconnectAction, RemoteControlActionExecutor.class);
        this.registerExecutor(OperationName.GetControlState, ReadExecutor.class);
        this.registerExecutor(OperationName.SetDcuCollScheme, DcuSetCollSchemeExecutor.class);
        this.registerExecutor(OperationName.SetDcuMeterTask, DcuSetMeterTaskExecutor.class);
        this.registerExecutor(OperationName.GetDcuCollScheme, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDcuMeterTask, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDcuClockOverThreshold, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDcuMonthOverThreshold, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDcuBroadcastTimeCheck, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDcuDeviceName, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDcuCommunicationAddr, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDcuGPRSParam, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDcuEthernetParam, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDcuBasicComParam, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDcuSignalStrength, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDcuLpDensity, ReadExecutor.class);
        this.registerExecutor(OperationName.SetDcuClockOverThreshold, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.SetDcuMonthOverThreshold, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.SetDcuBroadcastTimeCheck, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.SetDcuGPRSParam, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.SetDcuEthernetParam, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.SetDcuBasicComParam, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.SetDcuLpDensity, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.SetDcuReboot, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.SetNtpActivated, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.SetNtpServerAddr, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.SetNtpServerPort, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.GetNtpActivated, ReadExecutor.class);
        this.registerExecutor(OperationName.GetNtpServerAddr, ReadExecutor.class);
        this.registerExecutor(OperationName.GetNtpServerPort, ReadExecutor.class);
        this.registerExecutor(OperationName.SetGprsModemApn, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.SetDcuAutoConnect, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.GetGprsModemApn, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDcuAutoConnect, ReadExecutor.class);
        this.registerExecutor(OperationName.SetCalendarName, SetTouExecutor.class);
        this.registerExecutor(OperationName.SetDayProfile, SetTouExecutor.class);
        this.registerExecutor(OperationName.SetWeekProfile, SetTouExecutor.class);
        this.registerExecutor(OperationName.SetSeasonProfile, SetTouExecutor.class);
        this.registerExecutor(OperationName.SetActivateCalendarTime, SetTouExecutor.class);
        this.registerExecutor(OperationName.SetSpecialDays, SetTouExecutor.class);
        this.registerExecutor(OperationName.SetFriendlyDays, SetTouExecutor.class);
        this.registerExecutor(OperationName.SetMonitorValue, SetLoadLimitExecutor.class);
        this.registerExecutor(OperationName.SetThresholdNormal, SetLoadLimitExecutor.class);
        this.registerExecutor(OperationName.SetThresholdEmergency, SetLoadLimitExecutor.class);
        this.registerExecutor(OperationName.SetMinOverThresholdDuration, SetLoadLimitExecutor.class);
        this.registerExecutor(OperationName.SetMinUnderThresholdDuration, SetLoadLimitExecutor.class);
        this.registerExecutor(OperationName.SetEmergencyProfile, SetLoadLimitExecutor.class);
        this.registerExecutor(OperationName.SetEmergencyProfileGroupIDList, SetLoadLimitExecutor.class);
        this.registerExecutor(OperationName.SetLoadLimitActions, SetLoadLimitExecutor.class);
        this.registerExecutor(OperationName.GetCalendarNameActive, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDayProfileActive, ReadExecutor.class);
        this.registerExecutor(OperationName.GetWeekProfileActive, ReadExecutor.class);
        this.registerExecutor(OperationName.GetSeasonProfileActive, ReadExecutor.class);
        this.registerExecutor(OperationName.GetCalendarNamePassive, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDayProfilePassive, ReadExecutor.class);
        this.registerExecutor(OperationName.GetWeekProfilePassive, ReadExecutor.class);
        this.registerExecutor(OperationName.GetSeasonProfilePassive, ReadExecutor.class);
        this.registerExecutor(OperationName.GetActivateCalendarTime, ReadExecutor.class);
        this.registerExecutor(OperationName.GetSpecialDays, ReadExecutor.class);
        this.registerExecutor(OperationName.GetFriendlyDays, ReadExecutor.class);
        this.registerExecutor(OperationName.GetMonitorValue, ReadExecutor.class);
        this.registerExecutor(OperationName.GetThresholdNormal, ReadExecutor.class);
        this.registerExecutor(OperationName.GetThresholdEmergency, ReadExecutor.class);
        this.registerExecutor(OperationName.GetMinOverThresholdDuration, ReadExecutor.class);
        this.registerExecutor(OperationName.GetMinUnderThresholdDuration, ReadExecutor.class);
        this.registerExecutor(OperationName.GetEmergencyProfile, ReadExecutor.class);
        this.registerExecutor(OperationName.GetEmergencyProfileGroupIDList, ReadExecutor.class);
        this.registerExecutor(OperationName.GetLoadLimitActions, ReadExecutor.class);
        this.registerExecutor(OperationName.ReadEventProfile, ReadProfileExecutor.class);
        this.registerExecutor(OperationName.FtpParametersGet, ReadExecutor.class);
        this.registerExecutor(OperationName.FtpParametersSet, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.GetFirmWareVersion, GetFirmwareVersionExecutor.class);
        this.registerExecutor(OperationName.GeneralParamGet, GetGeneralParamExecutor.class);
        this.registerExecutor(OperationName.GeneralParamSet, SetGeneralParamExecutor.class);
        this.registerExecutor(OperationName.GeneralParamAction, ActionGeneralParamExecutor.class);
        this.registerExecutor(OperationName.SetMeterPayment, WriteExecutor.class);
        this.registerExecutor(OperationName.GetMeterPayment, GetGeneralParamExecutor.class);
        this.registerExecutor(OperationName.GetHeartBeatCycle, ReadExecutor.class);
        this.registerExecutor(OperationName.SetHeartBeatCycle, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.GetAutoRegisterMode, ReadExecutor.class);
        this.registerExecutor(OperationName.SetAutoRegisterMode, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.GetRefreshMeterStatus, ReadExecutor.class);
        this.registerExecutor(OperationName.SetRefreshMeterStatus, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.GetNetworkOnlineMeterList, ReadExecutor.class);
        this.registerExecutor(OperationName.GetNetWorkTopologyInformation, ReadExecutor.class);
        this.registerExecutor(OperationName.ActionKeyTransfer, KeyTransferActionExecutor.class);
        this.registerExecutor(OperationName.ActionKeyTransferConfirm, KeyTransferConfirmExecutor.class);
        this.registerExecutor(OperationName.SetTimeZone, SetClockObjectExecutor.class);
        this.registerExecutor(OperationName.SetDaylightSavingsBegin, SetClockObjectExecutor.class);
        this.registerExecutor(OperationName.SetDaylightSavingsEnd, SetClockObjectExecutor.class);
        this.registerExecutor(OperationName.SetDaylightSavingsDeviation, SetClockObjectExecutor.class);
        this.registerExecutor(OperationName.SetDaylightSavingsEnabled, SetClockObjectExecutor.class);
        this.registerExecutor(OperationName.SetTimeShiftEventLimit, SetClockObjectExecutor.class);
        this.registerExecutor(OperationName.SetTimeShiftInvalidLimit, SetClockObjectExecutor.class);
        this.registerExecutor(OperationName.GetTimeZone, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDaylightSavingsBegin, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDaylightSavingsEnd, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDaylightSavingsDeviation, ReadExecutor.class);
        this.registerExecutor(OperationName.GetDaylightSavingsEnabled, ReadExecutor.class);
        this.registerExecutor(OperationName.GetTimeShiftEventLimit, ReadExecutor.class);
        this.registerExecutor(OperationName.GetTimeShiftInvalidLimit, ReadExecutor.class);
        this.registerExecutor(OperationName.SetLocalSecretKey, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.SetProfileCapture, SetProfileCaptureExecutor.class);

        this.registerExecutor(OperationName.GetLpSetting, GetLpConfigExecutor.class);
        this.registerExecutor(OperationName.GetLpStatus, GetLpConfigExecutor.class);
        this.registerExecutor(OperationName.GetOffset, GetOffsetExecutor.class);
        this.registerExecutor(OperationName.AnsiReadLoadProfile, ReadAnsiProfileDataExecutor.class);
        this.registerExecutor(OperationName.AnsiReadLogBook, ReadAnsiLogBookExecutor.class);

        this.registerExecutor(OperationName.SetDcuPushStatus, DcuWriteDataExecutor.class);
        this.registerExecutor(OperationName.GetDcuPushStatus, ReadExecutor.class);
        this.registerExecutor(OperationName.AnsiTouSeason, GetTouSeasonExecutor.class);
        this.registerExecutor(OperationName.AnsiTouWeek, GetTouWeekExecutor.class);
        this.registerExecutor(OperationName.AnsiUpdateControl, UpdateControlExecutor.class);
    }

    public Boolean registerExecutor(OperationName name, Class executor) {
        if (executorMap.containsKey(name)) {
            return false;
        }
        executorMap.put(name, executor);
        return true;
    }

    public TaskExecutor getTaskExecutor(Long operationId) {
        DeviceOperation operationDict = assetService.getDictionary(operationId, DictionaryType.Operation.getType());
        if (operationDict == null) {
            log.error("Device operation not exists, id {} ", operationId);
            return null;
        }
        OperationName operationName = OperationName.forName(operationDict.getOptName());
        if (operationName == null) {
            log.error("operationType {} not exists in UTE", operationDict.getOptName());
            return null;
        }
        Class<TaskExecutor> executorClass = this.executorMap.get(operationName);
        try {
            TaskExecutor executor = executorClass.newInstance();
            autowireCapableBeanFactory.autowireBean(executor);
            return executor;
        } catch (InstantiationException | IllegalAccessException e) {
            log.error("instantiate class {} fail", executorClass.toString(), e);

        }
        return null;
    }
}
